﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using hlavny.modul2;
using System.IO;

namespace hlavny.modul4
{
    class DataManager
    {
        #region privatne atributy
        private CSysTables sysTables;
        private CSysColumns sysColumns;
        private int aktualnaTabulkaId;
        private string filename;
        private FileStream fileStream;
        private Blok vzorovyBlok;

        private int prechadzany = 0; // prechadzany zaznam (poradie od zaciatku suboru == rowId)
        #endregion



        #region properties
        public Blok VzorovyBlok
        {
            get { return vzorovyBlok; }
            set { vzorovyBlok = value; }
        }
        #endregion



        #region metody
        public DataManager(CSysTables paSysTables, CSysColumns paSysColumns, int paTabulkaId)
        {
            this.Init(paSysTables, paSysColumns, paTabulkaId);
        }

        private void Init(CSysTables paSysTables, CSysColumns paSysColumns, int paTabulkaId)
        {
            sysTables = paSysTables;
            sysColumns = paSysColumns;
            aktualnaTabulkaId = paTabulkaId;
            filename = this.GetFilename(paTabulkaId);
            vzorovyBlok = new Blok(sysTables, sysColumns, aktualnaTabulkaId);
            this.OpenFile();
            this.CloseFile();
        }

        public String GetFilename(int paTabulkaId)
        {
            return "data_" + paTabulkaId.ToString() + ".dat";
        }

        public Zaznam GetZaznam(int paRowId)
        {
            prechadzany = paRowId;
            return this.DajDalsi();
        }

        private void OpenFile()
        {
            try
            {
                if (File.Exists(filename))
                {
                    fileStream = File.Open(filename, FileMode.Open);
                }
                else
                {
                    fileStream = System.IO.File.Create(filename);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("chyba pri praci so suborom dat. " + ex.Message);
                throw ex;
            }
        }

        private void CloseFile()
        {
            fileStream.Close();
        }

        public long PridajPrazdnyBlok()
        {
            Blok novyBlok = new Blok(sysTables, sysColumns, aktualnaTabulkaId);
            return PridajExistujuciBlok(novyBlok);
        }

        public long PridajExistujuciBlok(Blok paUkladanyBlok, int paPoziciaBloku)
        {
            this.OpenFile();
            long poziciaBajtov = -12;
            if (paPoziciaBloku == int.MaxValue)
            {
                fileStream.Seek(0, SeekOrigin.End);
                poziciaBajtov = fileStream.Position;
            }
            else
            {
                poziciaBajtov = paPoziciaBloku * vzorovyBlok.VelkostBlokuBajtov;
                fileStream.Seek(poziciaBajtov, SeekOrigin.Current);
            }
            BinaryWriter binaryWriter = new BinaryWriter(fileStream);
            binaryWriter.Write(paUkladanyBlok.ToByte());
            binaryWriter.Close();
            this.CloseFile();
            return poziciaBajtov;
        }
        public long PridajExistujuciBlok(Blok paUkladanyBlok)
        {
            return PridajExistujuciBlok(paUkladanyBlok, int.MaxValue);
        }

        public long DajPocetBlokov()
        {
            System.IO.FileInfo fileInfo = new System.IO.FileInfo(filename);
            long pom = fileInfo.Length / this.vzorovyBlok.VelkostBlokuBajtov;
            return pom;
        }

        public long DajPocetMoznychZaznamov()
        {
            return this.DajPocetBlokov() * this.vzorovyBlok.VelkostBlokuZaznamov;
        }

        public Blok CitajBlok(int paPoradieBloku)
        {
            long adresaBloku = paPoradieBloku * vzorovyBlok.VelkostBlokuBajtov;
            this.OpenFile();
            fileStream.Seek(adresaBloku, SeekOrigin.Begin);
            BinaryReader binaryReader = new BinaryReader(fileStream);
            byte[] buffer = binaryReader.ReadBytes(vzorovyBlok.VelkostBlokuBajtov);
            this.CloseFile();
            if (buffer.Length == vzorovyBlok.VelkostBlokuBajtov)
            {
                return new Blok(this.sysTables, this.sysColumns, this.aktualnaTabulkaId, buffer);
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// Nastavi prechadzany blok na prvy blok v subore. Pouziva sa pred prvym volanim DajDalsiZaznam()
        /// </summary>
        public void VynulujAktualnuPoziciu()
        {
            prechadzany = 0;
        }

        public Zaznam DajDalsi()
        {
            Zaznam vystup = null;
            Blok blok = null;
            bool koniecSuboru = false;
            while (vystup == null && !koniecSuboru)
            {
                int poradieBlokuVSubore = prechadzany / vzorovyBlok.VelkostBlokuZaznamov;
                int poradieZaznamuVBloku = prechadzany % vzorovyBlok.VelkostBlokuZaznamov;
                Console.WriteLine("poradie Bloku : " + poradieBlokuVSubore);
                blok = CitajBlok(poradieBlokuVSubore);
                if (blok != null)
                {
                    int najdenyNaPozicii;
                    vystup = blok.DajDalsiZaznamZBloku(poradieZaznamuVBloku, out najdenyNaPozicii);
                    if (najdenyNaPozicii != -1)
                    { // ked som nasiel zaznam v bloku, tak dalsi hladany bude o 1 vacsi
                        vystup.RowId = poradieBlokuVSubore * vzorovyBlok.VelkostBlokuZaznamov + najdenyNaPozicii;
                        prechadzany = vystup.RowId + 1;
                    }
                    else
                    { // ked sa nenachadza v danom bloku uz ziaden platny zaznam, tak zvacsime blok
                        prechadzany = (poradieBlokuVSubore + 1) * vzorovyBlok.VelkostBlokuZaznamov;
                    }
                }
                else
                {
                    koniecSuboru = true;
                }
            }
            return vystup;
        }

        public int Vloz(Zaznam paZaznam)
        {
            int poradieVBloku = -1;
            int poradieBloku = -1;
            // nacitame posledny blok
            Blok blok;
            int pocetBlokov = (int)DajPocetBlokov();
            // ked je prazdny subor (novy)
            if (pocetBlokov == 0)
            {
                blok = new Blok(sysTables, sysColumns, aktualnaTabulkaId);
                blok.Add(paZaznam);
                PridajExistujuciBlok(blok);
                poradieVBloku = 0;
                poradieBloku = 0;
            }
            else
            {
                blok = CitajBlok(pocetBlokov - 1);
                if (blok.NasledujuciVolny < blok.VelkostBlokuZaznamov)
                {
                    blok.Add(paZaznam);
                    PridajExistujuciBlok(blok, pocetBlokov - 1);
                    poradieVBloku = blok.NasledujuciVolny - 1;
                    poradieBloku = pocetBlokov - 1;
                }
                else // ked nie je miesto v poslednom bloku
                {
                    blok = new Blok(sysTables, sysColumns, aktualnaTabulkaId);
                    blok.Add(paZaznam);
                    long poziciaBajtov = PridajExistujuciBlok(blok);
                    poradieVBloku = 0;
                    poradieBloku = pocetBlokov;
                }
            }
            return (poradieBloku * vzorovyBlok.VelkostBlokuZaznamov + poradieVBloku);
        }

        public retValueModul4 Vymaz(int paRowId)
        {
            int poradieBlokuVSubore = paRowId / vzorovyBlok.VelkostBlokuZaznamov;
            int poradieZaznamuVBloku = paRowId % vzorovyBlok.VelkostBlokuZaznamov;
            Blok blok = CitajBlok(poradieBlokuVSubore);
            if (blok != null)
            {
                
                try
                {
                    blok.Remove(poradieZaznamuVBloku);
                    PridajExistujuciBlok(blok, poradieBlokuVSubore);
                }
                catch (Exception)
                {
                    return retValueModul4.NeexistujuciZaznam;
                }
            }
            else
            {
                return retValueModul4.NeexistujuciBlok;
            }
            return retValueModul4.OK;
        }
        #endregion
    }
}
